import mongoose from "mongoose";
import type { Model } from "mongoose";
import type { Request } from "express";
import { commLog } from "../utils/log";

/** 根据_id查询资源选项 */
export interface IReqQueryByIdOptions {
  /** 资源ID */
  _id: string;
  /** 需要填充的字段，目前只支持1级填充，多个字段使用 空格 分隔 */
  fill?: string;
}

type TOptions = {
  queries: Record<string, string | [] | Object>;
  filters: Object;
};

/** 操作字符转义为mongodb关键字 */
const operatorConvert = (char: string) => {
  const operators: Record<string, string> = {
    ">": "$gt",
    gt: "$gt",
    ">=": "$gte",
    gte: "$gte",
    "<": "$lt",
    lt: "$lt",
    "<=": "$lte",
    lte: "$lte",
    "<>": "$ne",
    or: "$or",
    and: "$and",
    in: "$in",
    regex: "$regex",
    options: "$options",
  };
  return operators[char] || char;
};

/** 将对象中的运算符转换为mongodb关键字 */
const convertParamsToOptions = (obj: any): Record<string, any> => {
  const newObj: Record<string, any> = {};
  for (const key in obj) {
    if (Array.isArray(obj[key])) {
      if (typeof obj[key][0] !== "object")
        newObj[operatorConvert(key)] = obj[key];
      else newObj[operatorConvert(key)] = obj[key].map(convertParamsToOptions);
    } else if (typeof obj[key] === "object") {
      newObj[operatorConvert(key)] = convertParamsToOptions(obj[key]);
    } else {
      newObj[operatorConvert(key)] = obj[key];
    }
  }
  return newObj;
};

/** 构建列表查询参数 */
const buildOptions = (params: IReqQueryOptions): TOptions => {
  const queries: Record<string, string | string[] | Object> = params.query
    ? convertParamsToOptions(params.query)
    : {};
  const filters: Record<string, string | string[] | Object> = {};

  if (params.fill) {
    filters["populate"] = params.fill;
  }
  if (params.sort) {
    filters["sort"] = params.sort;
  }
  if (params.field) {
    filters["select"] = params.field;
  }
  if (params.paging) {
    const page = Math.max(params.paging.page ?? 1, 1);
    const pagesize = Math.max(params.paging.pagesize ?? 10, 1);
    filters["skip"] = (page - 1) * pagesize;
    filters["limit"] = pagesize;
  }

  return { queries, filters };
};

/**
 * 通用列表查询
 * @param model - 目标数据表的模型
 * @param parmas - req.body中的查询参数对象
 * @param onlyOne - 是否只返回一条数据，false-数组形式返回所有数据，true-对象形式返回单条数据，默认为false
 * @returns
 */
export const _list = async (
  model: Model<any>,
  params: IReqQueryOptions,
  onlyOne: boolean = false,
  session: mongoose.ClientSession | null = null
) => {
  const { queries, filters } = buildOptions(params);
  commLog.debug("queries=>", queries);
  commLog.debug("filters=>", filters);
  if (onlyOne)
    await model.findOne(queries).setOptions(filters).session(session).lean();
  else
    return await model
      .find(queries)
      .setOptions(filters)
      .session(session)
      .lean();
};

/** 通用指定id单条查询 */
export const _oneForId = async (
  model: Model<any>,
  idOptions: IReqQueryByIdOptions,
  session: mongoose.ClientSession | null = null
) => {
  const { _id, fill } = idOptions;
  if (fill)
    return await model.findById(_id).populate(fill).session(session).lean();
  else return await model.findById(_id).session(session).lean();
};

/** 创建单条数据 */
export const _createOne = async <T>(
  model: mongoose.Model<any>,
  data: T,
  session: mongoose.ClientSession | null = null
) => {
  const res = new model(data);
  await res.save({ session });
  return res;
};

/** 创建多条数据 */
export const _createMany = async <T extends Object[]>(
  model: mongoose.Model<any>,
  data: T,
  session: mongoose.ClientSession | null = null
) => {
  return await model.insertMany(data, { session });
};

/** 更新指定id的数据 */
export const _updateForId = async (
  model: Model<any>,
  _id: string,
  data: Object,
  session: mongoose.ClientSession | null = null
) => {
  return await model.findByIdAndUpdate(_id, data, { session, new: true });
};

/** 删除指定id的数据 */
export const _removeForId = async (
  model: Model<any>,
  _id: string,
  session: mongoose.ClientSession | null = null
) => {
  await model.findByIdAndDelete(_id).session(session);
  const count = await model.countDocuments({ _id }).session(session);
  return count === 0;
};

/**
 * 事务处理
 * @param req - 请求对象
 * @param fun - 事务逻辑函数
 * @param param - 事务逻辑函数的参数
 * @returns - 返回事务处理产生的数据对象。
 */
export const _transaction = async (
  req: Request,
  fun: Function,
  param: Object
) => {
  const session = await mongoose.startSession();

  req.dbSession = session;
  req.isDBTransaction = true;

  commLog.trace("事务开始");
  await session.startTransaction();

  // 这里执行逻辑
  let data = null;
  if (fun) {
    commLog.trace(`事务执行函数=>`, fun.name);
    data = await fun(param, session);
  }

  // 提交事务
  await session.commitTransaction();
  req.dbSession = null;
  req.isDBTransaction = false;
  commLog.trace(`事务成功提交并结束`);

  return data;
};
